package com.chenyun.gateway.dynamic;

import com.alibaba.cloud.nacos.NacosConfigProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.chenyun.gateway.dynamic.config.GatewayRouteConfigProperties;
import com.chenyun.gateway.dynamic.service.RouteService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 动态网关路由配置类
 * @author: Lonni
 * @date: 2022/11/18 0018 11:05
 */
@Component
@Slf4j
@RefreshScope
@AllArgsConstructor
@EnableConfigurationProperties(GatewayRouteConfigProperties.class)
@ConditionalOnProperty(value = "gateway.routes.type",havingValue = "dynamic")
public class DynamicGatewayRouteConfig implements CommandLineRunner {
   /*
      添加完成后使用actuator 查看路由配置http://localhost:10001/actuator/gateway/routes
    */


    /**
     * 存储动态添加的路由数据
     * 在生成swagger文档的时候需要获取
     */
    private static final List<RouteDefinition> DYNAMIC_ROUTE_LIST =new ArrayList<>();


    /**
     * 添加路由服务类
     */
    private final RouteService routeService;

    private final GatewayRouteConfigProperties routeConfigProperties;

    private final NacosConfigProperties nacosConfigProperties;

    @Override
    public void run(String... args) throws Exception {
       this.init();
    }


    public void init() throws NacosException {
        log.info("开始网关动态路由初始化...");
        Properties properties = new Properties();
        properties.setProperty("serverAddr", nacosConfigProperties.getServerAddr());
        properties.setProperty("namespace", nacosConfigProperties.getNamespace());
        // 创建nacos配置服务
        ConfigService configService = NacosFactory.createConfigService(properties);
        //第一次启动后加载路由
        String configInfo = configService.getConfig(routeConfigProperties.getDataId(),
                routeConfigProperties.getGroup(), nacosConfigProperties.getTimeout());
        log.info("第一次获取nacos路由配置,开始执行动态路由添加...");
        this.Route(configInfo);


        //监听配置变化
        configService.addListener(routeConfigProperties.getDataId(),
                routeConfigProperties.getGroup(),
                new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(String configInfo) {
                log.info("监听到配置文件变化....");
                DynamicGatewayRouteConfig.this.Route(configInfo);
            }
        });
    }


    /**
     * 转换路由配置并发布
     *
     * @param configInfo
     */
    public void Route(String configInfo) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            RouteDefinition[] routes = objectMapper.readValue(configInfo, RouteDefinition[].class);
            for (RouteDefinition route : routes) {
                routeService.updateRoute(route);
                this.addRoute(route);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error("转换路由配置失败:{}", ex.getMessage());
        }

    }

    /**
     * 将动态的路由添加到缓存中
     * @param routeDefinition 路由信息
     */
    void addRoute(RouteDefinition routeDefinition){
        final Optional<RouteDefinition> findRouteOptional = DYNAMIC_ROUTE_LIST.stream()
                .filter(p -> p.getId().equals(routeDefinition.getId()))
                .findFirst();
        findRouteOptional.ifPresent(DYNAMIC_ROUTE_LIST::remove);
        DYNAMIC_ROUTE_LIST.add(routeDefinition);

    }


    public List<RouteDefinition> getDynamicRouteList(){
        return DYNAMIC_ROUTE_LIST;
    }
}



